package com.wex.cookbook.security;

import cn.hutool.json.JSONUtil;
import com.wex.cookbook.entity.Userinfo;
import com.wex.cookbook.exception.BizAssertUtils;
import com.wex.cookbook.utils.JWTUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.stream.Stream;

import static com.wex.cookbook.constant.RoleEnum.RoleEnum.ADMIN;
import static com.wex.cookbook.constant.RoleEnum.RoleEnum.USER;

/**
 * token过滤器
 * @author : wex
 * @createTime : 2023/10/17 10:55
 */
@Slf4j
@Component
public class AuthenticationTokenFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        // 判断是否是允许的请求
        boolean existsUri = Stream.of(JwtTokenSecurityConfig.ALLOW_ASK)
                .flatMap(Stream::of)
                .anyMatch(item -> item.contains(request.getRequestURI()));
        if(existsUri){
            filterChain.doFilter(request, response);
            return;
        }

        // 获取token
        String token = request.getHeader("Authorization");
        if (StringUtils.isBlank(token)) {
            token = request.getParameter("token");
        }

        // 没有token
        if (StringUtils.isBlank(token)) {
            filterChain.doFilter(request, response);//放行,因为后面的会抛出相应的异常
            return;
        }

        // 非法token
        Userinfo userinfo;
        try {
            Claims claims = JWTUtils.parseToken(token);
            String user = claims.getSubject();
            userinfo = JSONUtil.toBean(user,Userinfo.class);
        } catch (Exception e) {
            log.error("非法token", e);
            throw new RuntimeException("非法token!");
        }

        String username = userinfo.getUsername();
        //判断该账号是否在线 不在线则不能访问该请求
        boolean online = JwtTokenSecurityConfig.accountOnlineList.stream()
                .anyMatch(item -> item.equalsIgnoreCase(username));
        BizAssertUtils.isTrue(online,"用户不在线请登录");

        //
        if(SecurityContextHolder.getContext().getAuthentication() == null){
            if(userinfo.getRole() == 1) {
                //构建用户信息-普通用户
                UserDetails role_admin = User.builder()
                        .username(userinfo.getUsername())
                        .password(userinfo.getPassword())
                        .authorities(new SimpleGrantedAuthority(USER.getMsg()))
                        .roles(USER.getMsg())
                        .build();
                // 将Authentication对象（用户信息、已认证状态、权限信息）存入 SecurityContextHolder
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(role_admin, null, role_admin.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            } else {
                //构建用户信息-管理员
                UserDetails role_user = User.builder()
                        .username(userinfo.getUsername())
                        .password(userinfo.getPassword())
                        .authorities(new SimpleGrantedAuthority(ADMIN.getMsg()))
                        .roles(ADMIN.getMsg())
                        .build();
                // 将Authentication对象（用户信息、已认证状态、权限信息）存入 SecurityContextHolder
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(role_user, null, role_user.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }

        //放行
        filterChain.doFilter(request, response);
    }
}
